﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace DrivingGame
{
    class City
    {
        private int[,] floorPlan = new int[,]
        {
            {1,2,3,5,2,1,4,3,1,3,2,4,5,1,2},
            {2,0,0,0,0,0,0,0,0,0,0,0,0,0,3},
            {5,0,3,2,1,3,4,5,2,5,4,2,1,0,4},
            {3,0,3,0,0,0,0,0,0,0,0,0,4,0,3},
            {5,0,4,0,2,1,2,3,5,4,3,0,2,0,1},
            {3,0,2,0,4,0,0,0,0,0,2,0,3,0,4},
            {4,0,5,0,3,0,3,2,5,0,5,0,3,0,3},
            {1,0,4,0,4,0,1,0,0,0,4,0,1,0,2},
            {2,0,2,0,1,0,2,1,3,5,1,0,3,0,5},
            {4,0,3,0,5,0,0,0,0,0,0,0,2,0,3},
            {1,0,5,0,2,1,2,1,3,5,2,1,5,0,1},
            {4,0,4,0,0,0,0,0,0,0,0,0,0,0,2},
            {3,0,4,2,3,1,2,3,5,4,1,3,5,2,4},
            {2,0,0,0,0,0,0,0,0,0,0,0,0,0,4},
            {5,1,2,2,4,3,3,4,5,2,5,1,1,3,5}
        };

        private int[] buildingHeights = new int[] { 0, 2, 3, 6, 5, 4 };
        private VertexBuffer vertexBuffer;
        private Texture2D groundTexture;
        private Texture2D finalSquareTexture;
        private float scale = 30.0f;
        private VertexPositionNormalTexture[] finalSquareVertices = new VertexPositionNormalTexture[6];

        public static GraphicsDevice device;
        public static BasicEffect basicEffect;
        
        public List<VertexPositionNormalTexture> verticesList { get; protected set; }
        public BoundingBox[] buildingBoundingBoxes { get; protected set; }
        

        public City(Texture2D groundTexture, Texture2D finalSquareTexture)
        {
            this.groundTexture = groundTexture;
            this.finalSquareTexture = finalSquareTexture;

            verticesList = new List<VertexPositionNormalTexture>();

            SetUpVertices();

            SetUpBoundingBoxes();
        }

        private void SetUpVertices()
        {
            int differentBuildings = buildingHeights.Length - 1;
            float imagesInTexture = 1 + differentBuildings * 2;

            int cityWidth = floorPlan.GetLength(0);
            int cityLength = floorPlan.GetLength(1);

            for (int x = 0; x < cityWidth; x++)
            {
                for (int z = 0; z < cityLength; z++)
                {
                    int currentbuilding = floorPlan[x, z];

                    //floor or ceiling
                    verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z), new Vector3(0, 1, 0), new Vector2(currentbuilding * 2 / imagesInTexture, 1)));
                    verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z - 1), new Vector3(0, 1, 0), new Vector2((currentbuilding * 2) / imagesInTexture, 0)));
                    verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[currentbuilding], -z), new Vector3(0, 1, 0), new Vector2((currentbuilding * 2 + 1) / imagesInTexture, 1)));

                    verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z - 1), new Vector3(0, 1, 0), new Vector2((currentbuilding * 2) / imagesInTexture, 0)));
                    verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[currentbuilding], -z - 1), new Vector3(0, 1, 0), new Vector2((currentbuilding * 2 + 1) / imagesInTexture, 0)));
                    verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[currentbuilding], -z), new Vector3(0, 1, 0), new Vector2((currentbuilding * 2 + 1) / imagesInTexture, 1)));

                    if (currentbuilding != 0)
                    {
                        //front wall
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z - 1), new Vector3(0, 0, -1), new Vector2((currentbuilding * 2) / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z - 1), new Vector3(0, 0, -1), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z - 1), new Vector3(0, 0, -1), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 1)));

                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z - 1), new Vector3(0, 0, -1), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z - 1), new Vector3(0, 0, -1), new Vector2((currentbuilding * 2) / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[currentbuilding], -z - 1), new Vector3(0, 0, -1), new Vector2((currentbuilding * 2) / imagesInTexture, 0)));

                        //back wall
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z), new Vector3(0, 0, 1), new Vector2((currentbuilding * 2) / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z), new Vector3(0, 0, 1), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z), new Vector3(0, 0, 1), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 0)));

                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z), new Vector3(0, 0, 1), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[currentbuilding], -z), new Vector3(0, 0, 1), new Vector2((currentbuilding * 2) / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z), new Vector3(0, 0, 1), new Vector2((currentbuilding * 2) / imagesInTexture, 1)));

                        //left wall
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z), new Vector3(-1, 0, 0), new Vector2((currentbuilding * 2) / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z - 1), new Vector3(-1, 0, 0), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z - 1), new Vector3(-1, 0, 0), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 0)));

                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z - 1), new Vector3(-1, 0, 0), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, buildingHeights[currentbuilding], -z), new Vector3(-1, 0, 0), new Vector2((currentbuilding * 2) / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x, 0, -z), new Vector3(-1, 0, 0), new Vector2((currentbuilding * 2) / imagesInTexture, 1)));

                        //right wall
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z), new Vector3(1, 0, 0), new Vector2((currentbuilding * 2) / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[currentbuilding], -z - 1), new Vector3(1, 0, 0), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z - 1), new Vector3(1, 0, 0), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 1)));

                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[currentbuilding], -z - 1), new Vector3(1, 0, 0), new Vector2((currentbuilding * 2 - 1) / imagesInTexture, 0)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, 0, -z), new Vector3(1, 0, 0), new Vector2((currentbuilding * 2) / imagesInTexture, 1)));
                        verticesList.Add(new VertexPositionNormalTexture(new Vector3(x + 1, buildingHeights[currentbuilding], -z), new Vector3(1, 0, 0), new Vector2((currentbuilding * 2) / imagesInTexture, 0)));
                    }
                }
            }

            //Set final square vertices
            finalSquareVertices[0] = new VertexPositionNormalTexture(new Vector3(7, 0.0f,-8), Vector3.Up, new Vector2(0.0f, 0.0f));
            finalSquareVertices[1] = new VertexPositionNormalTexture(new Vector3(8, 0.0f,-8), Vector3.Up, new Vector2(1.0f, 0.0f));
            finalSquareVertices[2] = new VertexPositionNormalTexture(new Vector3(8, 0.0f,-7), Vector3.Up, new Vector2(1.0f, 1.0f));
            finalSquareVertices[3] = new VertexPositionNormalTexture(new Vector3(8, 0.0f, -7), Vector3.Up, new Vector2(1.0f, 1.0f));
            finalSquareVertices[4] = new VertexPositionNormalTexture(new Vector3(7, 0.0f,-7), Vector3.Up, new Vector2(0.0f, 1.0f));
            finalSquareVertices[5] = new VertexPositionNormalTexture(new Vector3(7, 0.0f, -8), Vector3.Up, new Vector2(0.0f, 0.0f));
        }

        private void SetUpBoundingBoxes()
        {
            int cityWidth = floorPlan.GetLength(0);
            int cityLength = floorPlan.GetLength(1);

            List<BoundingBox> bbList = new List<BoundingBox>(); for (int x = 0; x < cityWidth; x++)
            {
                for (int z = 0; z < cityLength; z++)
                {
                    int buildingType = floorPlan[x, z];
                    if (buildingType != 0)
                    {
                        int buildingHeight = buildingHeights[buildingType];
                        Vector3[] buildingPoints = new Vector3[2];
                        buildingPoints[0] = new Vector3(x * scale, 0, -z * scale);
                        buildingPoints[1] = new Vector3((x + 1) * scale, buildingHeight * scale, (-z - 1) * scale);
                        BoundingBox buildingBox = BoundingBox.CreateFromPoints(buildingPoints);
                        bbList.Add(buildingBox);
                    }
                }
            }
            buildingBoundingBoxes = bbList.ToArray();
        }

        public void Draw(Matrix view, Matrix projection)
        {

            basicEffect.EnableDefaultLighting();
            basicEffect.World = Matrix.Identity * Matrix.CreateScale(scale);
            basicEffect.View = view;
            basicEffect.Projection = projection;
            basicEffect.Texture = this.groundTexture;
            basicEffect.TextureEnabled = true;

            vertexBuffer = new VertexBuffer(device, typeof(VertexPositionNormalTexture), verticesList.ToArray().Length, BufferUsage.None);
            vertexBuffer.SetData(verticesList.ToArray());

            foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
            {
                pass.Apply();

                device.DrawUserPrimitives<VertexPositionNormalTexture>
                    (PrimitiveType.TriangleList, verticesList.ToArray(), 0, vertexBuffer.VertexCount / 3);

            }

            basicEffect.Texture = this.finalSquareTexture;
            vertexBuffer = new VertexBuffer(device, typeof(VertexPositionNormalTexture), finalSquareVertices.Length, BufferUsage.None);
            vertexBuffer.SetData(finalSquareVertices);

            foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
            {
                pass.Apply();

                device.DrawUserPrimitives<VertexPositionNormalTexture>
                    (PrimitiveType.TriangleList, finalSquareVertices, 0, 2);

            }
        }
    }
}
